package conf

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"time"

	"chainmaker.org/chainmaker/smarteditor/cache"
	"chainmaker.org/chainmaker/smarteditor/common"
	"chainmaker.org/chainmaker/smarteditor/db"
	"chainmaker.org/chainmaker/smarteditor/util"
	"github.com/jinzhu/gorm"
)

type UserDeployed struct {
	UserId      string `gorm:"index:idx_id"`
	HasDeployed bool
}

func (*UserDeployed) TableName() string {
	return "user_deployed"
}

func GenerateUseDeployedKey(userId string) string {
	return fmt.Sprintf("deployed_%s", userId)
}

// CheckUserHasDeployed 检查一下该用户是否已经读取过历史的部署信息
func CheckUserHasDeployed(userId string) (bool, error) {
	deKey := GenerateUseDeployedKey(userId)
	_, ok := cache.GlobalUserCache.Get(deKey)
	if ok {
		return true, nil
	}
	var retD UserDeployed
	err := db.GlobalDB.Model(UserDeployed{}).Where("user_id = ?", userId).First(&retD).Error
	if err != nil {
		if gorm.IsRecordNotFoundError(err) {
			return false, nil
		}
		log.Errorf("CheckUserHasDeployed userId %s failed %s", userId, err.Error())
		return false, err
	}
	cache.GlobalUserCache.Add(deKey, struct{}{})
	return true, nil
}

// SaveUserHasDeployed 记录该用户已经写入了deployinfo信息
func SaveUserHasDeployed(userId string) error {
	deKey := GenerateUseDeployedKey(userId)
	err := db.GlobalDB.Create(UserDeployed{UserId: userId, HasDeployed: true}).Error
	if err != nil {
		log.Errorf("SaveUserHasDeployed userId %s,failed %s", userId, err.Error())
		return err
	}
	cache.GlobalUserCache.Add(deKey, struct{}{})
	return nil
}

type DeployInfo struct {
	Date  string     `json:"date,omitempty"`
	BInfo *BuildInfo `json:"build_info,omitempty"`
}

type BuildInfo struct {
	ContractName   string   `json:"contract_name,omitempty"`
	Date           string   `json:"date,omitempty"`
	Hash           string   `json:"hash,omitempty"`
	Methods        []string `json:"methods,omitempty"`
	SourceCodeHash string   `json:"source_code_hash"`
}

func ReadBuildInfo(buildInfoPath string) (*BuildInfo, error) {
	buildInfoData, err := ioutil.ReadFile(buildInfoPath)
	if err != nil {
		return nil, err
	}
	result := BuildInfo{}
	err = json.Unmarshal(buildInfoData, &result)
	return &result, err
}

func ReadDeployInfo(deployInfoPath string) ([]DeployInfo, error) {
	deployInfoList := make([]DeployInfo, 0)
	if !util.FileExists(deployInfoPath) {
		return deployInfoList, nil
	}

	deployInfoData, err := ioutil.ReadFile(deployInfoPath)
	if err != nil {
		return nil, err
	}
	if len(deployInfoData) == 0 {
		return deployInfoList, nil
	}
	err = json.Unmarshal(deployInfoData, &deployInfoList)
	return deployInfoList, err
}

func (origin *DeployInfo) TransformToDeployInfoModel(userId string) *DeployInfoModel {
	var ret DeployInfoModel
	ret.UserId = userId
	ret.SrcMd5 = origin.BInfo.SourceCodeHash
	ret.Methods = origin.BInfo.Methods
	nowTs := time.Now().Unix()
	deployedT, err := time.Parse(common.TimeLayout, origin.Date)
	if err == nil {
		ret.DeployTime = deployedT.Unix()
	} else {
		ret.DeployTime = nowTs
	}
	ret.ContractName = origin.BInfo.ContractName
	buildT, err := time.Parse(common.TimeLayout, origin.BInfo.Date)
	if err == nil {
		ret.BuildTime = buildT.Unix()
	} else {
		ret.BuildTime = nowTs
	}
	ret.BinMd5 = origin.BInfo.Hash
	return &ret
}
